// ==UserScript==
// @name         油猴中文网-定时每日签到
// @namespace    https://bbs.tampermonkey.net.cn/
// @version      1.1.1
// @description  油猴中文网-每天定时自动完成网站的每日签到功能！
// @author       Free-wang 完成主要代码、逻辑思路、以及整体架构的开发
// @contributor  DeepSeek 提供部分代码、逻辑分析、以及语法设计的辅助
// @license      MIT
// @supportURL   https://scriptcat.org/zh-CN/script-show-page/3675/issue
// @website      https://gitee.com/ihacker/UserScripts/
// @source       https://gitee.com/ihacker/UserScripts/blob/master/crontab-user-script/%E6%B2%B9%E7%8C%B4%E4%B8%AD%E6%96%87%E7%BD%91-%E5%AE%9A%E6%97%B6%E6%AF%8F%E6%97%A5%E7%AD%BE%E5%88%B0.js
// @icon         https://img.icons8.com/keek/100/ok-hand.png
// @crontab      * 0-23 once * *
// @grant        GM_xmlhttpRequest
// @grant        GM_notification
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_info
// @grant        GM_log
// @connect      scriptcat.org
// @connect      bbs.tampermonkey.net.cn
// @require      https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.min.js
// @require      https://scriptcat.org/lib/881/1.2.0/script-statistics.js
// @require      https://cdn.jsdelivr.net/npm/scriptcat-lib@1.1.6/dist/scsite.js
// @antifeature  tracking 统计用户安装使用情况
// @antifeature  annoyances 每次都引导至脚本站评分
// @antifeature  referral-link 自动引导至脚本站评分页
// ==/UserScript==

/* ==UserConfig==
SignConfig:
  configA:
    title: 签到模式选择
    description: 今日最想说模式：1-自己填写、2-快速选择(默认)、3-不想填写
    type: select
    default: 2
    labels: ['自己填写','快速选择','不想填写']
    values: [1,2,3]
  configB:
    title: 签到自定义语句
    description: 我今天最想说
    type: text
    default: 我今天最想说：我什么都不想说！
    min: 3
    max: 50
    password: false
 ==/UserConfig== */

return new Promise((resolve, reject) => {
    // 表情列表
    const EMOTIONS = ['kx', 'ng', 'ym', 'wl', 'nu', 'ch', 'fd', 'yl', 'shuai'];

    // 快速选择模式的快速语句选择
    const QUICK_PHRASE = ['0', '1', '2', '3', '4', '5', '6', '7'];

    // 默认配置
    const DEFAULT_CONFIG = {
        signMode: GM_getValue("SignConfig.configA"), // 1=自己填写, 2=快速选择, 3=不想填写
        customWantSaying: GM_getValue("SignConfig.configB") // 自己填写模式的内容
    };

    /**
     * 随机选择函数：用于随机选择EMOTIONS(表情)和QUICK_PHRASE(快速语句)
     */
    function getRandomItem(array) {
        return array[Math.floor(Math.random() * array.length)];
    }

    /**
     * 发送请求函数：用于请求[首页(判断是否未登录)]、[签到页(判断是否已签到)]、[签到提交(提交签到请求)]
     */
    function fetchUrl(url, method = 'GET', data = null) {
        return new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                method: method,
                url: url,
                data: data,
                headers: {
                    'Content-Type': 'application/x-www-form-urlencoded'
                },
                onload: (response) => {
                    if (response.status >= 200 && response.status < 400) {
                        resolve({text: response.responseText, status: response.status});
                    } else {
                        reject(new Error(`网络请求状态码异常-${response.status}`));
                    }
                },
                onerror: (error) => {
                    reject(new Error(`网络请求异常-${error}`));
                }
            });
        });
    }

    /**
     * 检查是否未登录：因油猴中文网滑动验证无法实现自动登录，所以需要提前登录好帐号！
     * @return {boolean} 是否未登录：true-未登录、false-已登录
     */
    async function isNotLogin() {
        try {
            const result = await fetchUrl('https://bbs.tampermonkey.net.cn/');
            const parser = new DOMParser();
            const document = parser.parseFromString(result.text, 'text/html');

            // 检查[登录链接]或[立即注册]按钮是否存在，判断方式：根据href链接判断
            const loginLink = document.querySelector('a[href="member.php?mod=logging&action=login"], a[href="member.php?mod=register"]');
            // 存在[登录链接]或[立即注册]按钮则代表未登录【注：双感叹号用于将结果隐式转换布尔类型，类似于`return Boolean(loginLink);`】
            return !!loginLink;
        } catch (error) {
            throw new Error(`登录状态检查失败：${error}`);
        }
    }

    /**
     * 检查签到状态：用于判断是否已签到，已经签到的提示并结束脚本！
     * @return {boolean} 是否已签到：true-已签到、false-未签到
     */
    async function checkSignStatus() {
        let isSigned = false;
        try {
            const result = await fetchUrl('https://bbs.tampermonkey.net.cn/dsu_paulsign-sign.html');
            const parser = new DOMParser();
            const document = parser.parseFromString(result.text, 'text/html');

            // 情况1：检查已签到标题
            const signedTitle = document.querySelector('h1.mt');
            if (signedTitle && signedTitle.textContent.includes('您今天已经签到过了')) {
                isSigned = true;
            }
            // 情况2：检查服务台区域
            const serviceArea = document.getElementById('qdmsgt');
            if (serviceArea && !serviceArea.textContent.includes('今天未签到')) {
                isSigned = true;
            }

            return isSigned;
        } catch (error) {
            throw new Error(`签到状态检查失败：${error}`);
        }
    }

    /**
     * 执行签到操作：填写表单、并发起签到请求
     * @throws {Error} 抛出异常：签到执行失败
     */
    async function performSign() {
        try {
            // 获取签到页面获取formhash
            const signPage = await fetchUrl('https://bbs.tampermonkey.net.cn/dsu_paulsign-sign.html');
            const parser = new DOMParser();
            const document = parser.parseFromString(signPage.text, 'text/html');

            // 获取formhash
            const formhashInput = document.querySelector('input[name="formhash"]');
            const formhash = formhashInput.value;

            // 获取配置
            const signMode = GM_getValue('signMode', DEFAULT_CONFIG.signMode);
            const customWantSaying = GM_getValue('customWantSaying', DEFAULT_CONFIG.customWantSaying);

            // 准备签到数据
            let postData = `formhash=${encodeURIComponent(formhash)}`;

            // 添加表情参数
            const randomEmotion = getRandomItem(EMOTIONS);
            postData += `&qdxq=${encodeURIComponent(randomEmotion)}`;

            // 添加模式参数
            postData += `&qdmode=${encodeURIComponent(signMode)}`;

            // 添加模式内容
            if (signMode === 1) {
                // 自己填写模式
                postData += `&todaysay=${encodeURIComponent(customWantSaying.substring(0, 50))}`;
            } else if (signMode === 2) {
                // 快速选择模式
                const randomPhrase = getRandomItem(QUICK_PHRASE);
                postData += `&fastreply=${encodeURIComponent(randomPhrase)}`;
            }

            // 执行签到请求
            const signUrl = 'https://bbs.tampermonkey.net.cn/plugin.php?id=dsu_paulsign:sign&operation=qiandao&infloat=1';
            await fetchUrl(signUrl, 'POST', postData);

            return true;
        } catch (error) {
            throw new Error(`签到执行失败：${error}`);
        }
    }

    /**
     * 主执行函数：用于脚本执行时启动
     */
    async function main() {
        try {
            // 【功能研究】弹出引导窗口，前往脚本站进行评分
            const guide = new scsite.guide(1);
            guide.auto();
            // 【功能研究】统计脚本的用户使用情况信息
            try {
                new SC_Statistic({
                    key: "d2jvfy1rohhto9uu",
                });
            } catch (error) {
                GM_log(`《${GM_info.script.name}》脚本的用户使用情况统计异常：${error}`, "warn");
                console.warn("脚本的用户使用情况统计异常：", error);
            }

            // 1. 检查登录状态
            const isNotLogged = await isNotLogin();
            if (isNotLogged) {
                const prompt = "您还未登录，自动签到需手动提前登录好，请登录后脚本将在2小时后重新执行！";
                GM_log(`《${GM_info.script.name}》脚本提示：${prompt}`, "info");
                GM_notification({
                    title: `《${GM_info.script.name}》脚本提示`,
                    text: prompt,
                    requireInteraction: true
                });
                // 未登录算脚本执行失败，以便脚本能够再次重新尝试执行！【当前设定为7200秒(即2小时后)后重试】
                return reject(new CATRetryError("您还未登录油猴中文网，无法自动签到！", 7200));
            }

            // 2. 检查签到状态
            const hasSigned = await checkSignStatus();
            if (hasSigned) {
                const prompt = "今日已签到，无需重复签到！";
                GM_log(`《${GM_info.script.name}》脚本提示：${prompt}`, "info");
                GM_notification({
                    title: `《${GM_info.script.name}》脚本提示`,
                    text: prompt,
                    requireInteraction: true
                });
                return resolve("脚本运行完毕");
            }

            // 3. 执行签到
            const signResult = await performSign();
            if (signResult) {
                // 验证签到结果
                const verifySign = await checkSignStatus();
                if (verifySign) {
                    const prompt = "定时自动签到脚本执行成功！";
                    GM_log(`《${GM_info.script.name}》脚本提示：${prompt}`, "info");
                    GM_notification({
                        title: `《${GM_info.script.name}》脚本提示`,
                        text: prompt,
                        requireInteraction: true
                    });
                } else {
                    const prompt = "网站有更新，导致【检查签到】或【执行签到】方法异常，需重新适配！";
                    GM_log(`《${GM_info.script.name}》脚本提示：${prompt}`, "info");
                    GM_notification({
                        title: `《${GM_info.script.name}》脚本提示`,
                        text: prompt,
                        requireInteraction: true
                    });
                }
            }
            resolve("脚本运行完毕");
        } catch (error) {
            GM_log(`${GM_info.script.name}脚本错误：${error}`, "error");
            GM_notification({
                title: `《${GM_info.script.name}》脚本提示`,
                text: `${GM_info.script.name}脚本执行失败，请进入脚本猫中查看详细错误日志！`,
                requireInteraction: true
            });
            reject(error);
        }
    }

    // 启动主函数
    main().then(r => resolve(r)).catch(e => reject(e));
});